package com.blog.auth.service.impl;

import com.alibaba.fastjson.JSON;
import com.blog.auth.dao.AuthUserDao;
import com.blog.auth.pojo.AuthUser;
import com.blog.auth.service.AuthService;
import com.blog.auth.util.AuthToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/*****
 * @Author: www.itheima
 * @Date: 2019/7/7 16:23
 * @Description: com.changgou.oauth.service.impl
 ****/
@Service
public class AuthServiceImpl implements AuthService {

    @Value("${auth.ttl}")
    private long tokenTtl;

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Autowired
    AuthUserDao authUserDao;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    private RestTemplate restTemplate;

    /***
     * 授权认证方法
     * @param username
     * @param password
     * @param clientId
     * @param clientSecret
     * @return
     */
    @Override
    public AuthToken login(String username, String password, String clientId, String clientSecret) {
        //申请令牌
        AuthToken authToken = applyToken(username,password,clientId, clientSecret);
        if(authToken == null){
            throw new RuntimeException("申请令牌失败");
        }
        // 用户身份令牌
        String accessToken = authToken.getAccessToken();
        // 存储redis中的内容
        String jsonString = JSON.toJSONString(accessToken);
        // 将令牌存储到redis
        boolean result = saveToken(accessToken, jsonString, tokenTtl);
        if (!result) {
            throw new RuntimeException("存储Redis令牌失败");
        }
        return authToken;
    }

    /**
     * 将令牌存储到redis
     * @param accessToken 用户身份令牌
     * @param content 内容 就是 AuthToken对象的内容
     * @param ttl 有效期
     * @return
     */
    private boolean saveToken(String accessToken, String content, long ttl) {
        String key = "user_token" + accessToken;
        redisTemplate.boundValueOps(key).set(content, ttl, TimeUnit.SECONDS);
        // 查询是否存储成功
        Long expire = redisTemplate.getExpire(key, TimeUnit.SECONDS);
        return expire>0;
    }

    @Override
    public AuthUser getUsername(String username) {
        return authUserDao.getUserByUsername(username);
    }

    /**
     * 认证方法 申请令牌
     * @param username:用户登录名字
     * @param password：用户密码
     * @param clientId：配置文件中的客户端ID
     * @param clientSecret：配置文件中的秘钥
     * @return
     */
    private AuthToken applyToken(String username, String password, String clientId, String clientSecret){
        //从eureka中获取认证服务的地址（因为spring security在认证服务中）
        //从eureka中获取认证服务的一个实例的地址
        ServiceInstance serviceInstance = loadBalancerClient.choose("auth-service");
        //此地址就是http://ip:port
        String uri = serviceInstance.getUri().toString();
        //令牌申请的地址 http://localhost:40400/auth/oauth/token
        String authUrl = uri+ "/oauth/token";
//        String path = serviceInstance.getUri().toString() + "/oauth/token";
        //定义header
        LinkedMultiValueMap<String, String> header = new LinkedMultiValueMap<>();
        String httpBasic = getHttpBasic(clientId, clientSecret);
        header.add("Authorization",httpBasic);

        //定义body
        LinkedMultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type","password");
        body.add("username",username);
        body.add("password",password);

        HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(body, header);
        //String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables

        //设置restTemplate远程调用时候，对400和401不让报错，正确返回数据
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){
            @Override
            public void handleError(ClientHttpResponse response) throws IOException {
                if(response.getRawStatusCode()!=400 && response.getRawStatusCode()!=401){
                    super.handleError(response);
                }
            }
        });

        ResponseEntity<Map> exchange = restTemplate.exchange(authUrl, HttpMethod.POST, httpEntity, Map.class);

        //申请令牌信息
        Map bodyMap = exchange.getBody();
        if(bodyMap == null ||
                bodyMap.get("access_token") == null ||
                bodyMap.get("refresh_token") == null ||
                bodyMap.get("jti") == null){
            return null;
        }
        AuthToken authToken = new AuthToken();
        authToken.setJti((String) bodyMap.get("jti"));//用户身份令牌
        authToken.setRefreshToken((String) bodyMap.get("refresh_token"));//刷新令牌
        authToken.setAccessToken((String) bodyMap.get("access_token"));//jwt令牌
        return authToken;
    }

    /***
     * base64编码
     * @param clientId
     * @param clientSecret
     * @return
     */
    private String getHttpBasic(String clientId,String clientSecret){
        //将客户端id和客户端密码拼接，按“客户端id:客户端密码”
        String string = clientId+":"+clientSecret;
        //进行base64编码
        byte[] encode = Base64Utils.encode(string.getBytes());
        return "Basic "+new String(encode);
    }
}
